---
title: LiveKit
sidebar_label: Home
slug: /
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

LiveKit is an open source project that provides scalable, multi-user conferencing over WebRTC. It's designed to give you everything you need to build real-time audio and/or video experiences in your applications.

## Features

- Horizontally-scalable WebRTC Selective Forwarding Unit (SFU)
- Modern, full-featured [client SDKs](/references/client-sdks) for JavaScript, React, Swift, Kotlin, and Flutter (React Native and Unity/C# in development)
- Built for production - JWT authentication and [server APIs](/guides/server-api)
- Robust networking & connectivity over UDP, TCP, and built-in [TURN](/deploy#improving-connectivity-with-turn)
- Easy to deploy: pure Go and single binary
- Advanced features like speaker detection, simulcast, selective subscription, and moderation APIs
<!-- - Effortless, customizable recording - use our built-in templates or record your own web UI -->

## Playground

Head to [our playground](https://livekit.io/playground) to try it live. Build a Zoom-like conferencing app in under 100 lines of code!

## Why LiveKit?

WebRTC is a powerful protocol that now has ubiquitous support across all major browsers and mobile platforms. However, building apps with it isn't simple; it requires an understanding of core protocol concepts and developers are responsible for complexities like signaling and coordinating connections between peers. As a peer-to-peer protocol, scaling WebRTC to large numbers of peers also becomes challenging.

While hosted solutions like Twilio and Agora exist, they can be costly, have limited flexibility and as proprietary products, create vendor lock-in. Other open source solutions also exist, but they have a steep learning curve and are daunting to customize and deploy.

We set out to build a free and open source implementation of WebRTC rooms that's easily embeddable within any app. LiveKit provides an opinionated, end-to-end real time communications solution with first-party SDKs for all the major software platforms.

## Architecture

LiveKit is written in Go. It's made possible by [Pion WebRTC](https://github.com/pion/webrtc), [ion-sfu](https://github.com/pion/ion-sfu), and the amazing community behind them.

LiveKit is horizontally-scalable. You can run it on one node or 100, with an identical configuration. Nodes use peer-to-peer routing via Redis to ensure clients in the same room are connected to the same node.

LiveKit has no external dependencies when running in single-node. Redis is required for a distributed setup spanning multiple nodes.

![LiveKit Architecture](/img/architecture.svg)

## Client Examples

<Tabs
  defaultValue="typescript"
  groupId="client-sdk"
  values={[
    {label: 'Browser', value: 'typescript'},
    {label: 'React', value: 'react'},
    {label: 'iOS/macOS', value: 'ios'},
    {label: 'Android', value: 'android'},
    {label: 'Flutter', value: 'flutter'},
    {label: 'Golang', value: 'go'},
  ]}>
  <TabItem value="typescript">

```typescript title="TypeScript"
import {
  connect,
  createLocalTracks,
  Participant,
  RoomEvent,
  Track,
  VideoTrack,
} from 'livekit-client'

const url = 'wss://your_host'
const token = 'jwt_token'

const room = await connect(url, token)

// set up listeners
room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
  attachTrack(track, participant)
})

const tracks = await createLocalTracks({
  audio: true,
  video: true,
})
for (let track of tracks) {
  // publish to the room
  const publication = await room.localParticipant.publishTrack(track)
  // attach to DOM only if it's video
  if (track instanceof VideoTrack) {
    attachTrack(track, room.localParticipant)
  }
}

function attachTrack(track: Track, participant: Participant) {
  // creates a new audio or video element
  const element = track.attach()
  // find the target element for participant
  ...
  target.appendChild(element)
}
```

  </TabItem>
  <TabItem value="react">

```typescript title="TypeScript"
import { LiveKitRoom } from 'livekit-react'

export const RoomPage = () => {
  const url = 'wss://your_host'
  const token = 'your_token'
  return (
    <div className="roomContainer">
      <LiveKitRoom url={url} token={token} onConnected={room => onConnected(room)}/>
    </div>
  )
}

function onConnected() {
  const audioTrack = await createLocalAudioTrack()
  await room.localParticipant.publishTrack(audioTrack)
  const videoTrack = await createLocalVideoTrack();
  await room.localParticipant.publishTrack(videoTrack)
}
```

  </TabItem>
  <TabItem value="ios">

```swift title="Swift"
import LiveKit
import UIKit

class RoomViewController: UIViewController {
    var room: Room?
    var remoteVideo: VideoView?
    var localVideo: VideoView?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        let url: String = "ws://your_host"
        let token: String = "your_jwt_token"

        room = LiveKit.connect(options: ConnectOptions(url: url, token: token), delegate: self)
    }

    func attachVideo(track: VideoTrack, participant: Participant) {
      let videoView = VideoView(frame: .zero)
      // find destination view
      ...
      target.addSubview(videoView)
      track.addRenderer(videoView.renderer)
    }
}

extension RoomViewController: RoomDelegate {
    func didConnect(room: Room) {
        guard let localParticipant = room.localParticipant else {
            return
        }

        // perform work in the background, to not block WebRTC threads
        DispatchQueue.global(qos: .background).async {
          do {
              let videoTrack = try LocalVideoTrack.createTrack(name: "localVideo")
              _ = localParticipant.publishVideoTrack(track: videoTrack)
              let audioTrack = LocalAudioTrack.createTrack(name: "localAudio")
              _ = localParticipant.publishAudioTrack(track: audioTrack)
          } catch {
              // error publishing
          }
        }

        // attach video view
        attachVideo(videoTrack, localParticipant)
    }

    func didSubscribe(track: Track, publication _: RemoteTrackPublication, participant _: RemoteParticipant) {
        guard let videoTrack = track as? VideoTrack else {
          return
        }
        DispatchQueue.main.async {
            attachVideo(videoTrack, participant)
        }
    }
}
```

  </TabItem>
  <TabItem value="android">


```kt title="Kotlin"

class MainActivity : AppCompatActivity(), RoomListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        val url = "wss://your_host";
        val token = "your_token"

        launch {
            val room = LiveKit.connect(
                applicationContext,
                url,
                token,
                ConnectOptions(),
                this
            )
            val localParticipant = room.localParticipant
            val audioTrack = localParticipant.createAudioTrack()
            localParticipant.publishAudioTrack(audioTrack)
            val videoTrack = localParticipant.createVideoTrack()
            localParticipant.publishVideoTrack(videoTrack)
            videoTrack.startCapture()

            attachVideo(videoTrack, localParticipant)
        }
    }

    override fun onTrackSubscribed(
        track: Track,
        publication: RemoteTrackPublication,
        participant: RemoteParticipant,
        room: Room
    ) {
        if (track is VideoTrack) {
            attachVideo(track, participant)
        }
    }

    private fun attachVideo(videoTrack: VideoTrack, participant: Participant) {
        // viewBinding.renderer is a `org.webrtc.SurfaceViewRenderer` in your
        // layout
        videoTrack.addRenderer(viewBinding.renderer)
    }
}

```

  </TabItem>
  <TabItem value="flutter">

```dart title="Dart"
var url = 'your-host';
var token = 'your-token';
var room = await LiveKitClient.connect(url, token);
try {
  // video will fail when running in ios simulator
  var localVideo = await LocalVideoTrack.createCameraTrack();
  await room.localParticipant.publishVideoTrack(localVideo);
} catch (e) {
  print('could not publish video: $e');
}

var localAudio = await LocalAudioTrack.createTrack();
await room.localParticipant.publishAudioTrack(localAudio);
```

  </TabItem>
<TabItem value="go">

```go
import (
  lksdk "github.com/livekit/server-sdk-go"
)

// Go SDK is a more advanced SDK that gives you programmatic access as a client
// enabling you to publish and record audio/video/data to the room.
func main() {
  host := "<host>"
  apiKey := "api-key"
  apiSecret := "api-secret"
  roomName := "myroom"
  identity := "botuser"
	room, err := lksdk.ConnectToRoom(host, lksdk.ConnectInfo{
		APIKey:              apiKey,
		APISecret:           apiSecret,
		RoomName:            roomName,
		ParticipantIdentity: identity,
	})
	if err != nil {
		panic(err)
	}

  room.Callback.OnTrackSubscribed = trackSubscribed
  ...
  room.Disconnect()
}

func trackSubscribed(track *webrtc.TrackRemote, publication lksdk.TrackPublication, rp *lksdk.RemoteParticipant) {

}
```

</TabItem>
</Tabs>
